Micron Document
πŸŽ–οΈGitΠ―Ρ€Π°πŸŽ–οΈ


Displaying Raw β€’ Download

feature/connections/src/commonMain/kotlin/org/meshtastic/feature/connections/ui/ConnectionsScreen.kt 8f9eec5c0819ec4d9b8a69e6dfc7aaacd86e30a6 (8f9eec5c) Text, 29.81 KB

T8b949e/*
* Copyright (c) 2026 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
Tff7b72package T7ee787org.meshtastic.feature.connections.ui

Tff7b72import T7ee787androidx.compose.animation.AnimatedContent
Tff7b72import T7ee787androidx.compose.animation.animateContentSize
Tff7b72import T7ee787androidx.compose.animation.fadeIn
Tff7b72import T7ee787androidx.compose.animation.fadeOut
Tff7b72import T7ee787androidx.compose.animation.togetherWith
Tff7b72import T7ee787androidx.compose.foundation.layout.Arrangement
Tff7b72import T7ee787androidx.compose.foundation.layout.Box
Tff7b72import T7ee787androidx.compose.foundation.layout.Column
Tff7b72import T7ee787androidx.compose.foundation.layout.Spacer
Tff7b72import T7ee787androidx.compose.foundation.layout.fillMaxSize
Tff7b72import T7ee787androidx.compose.foundation.layout.fillMaxWidth
Tff7b72import T7ee787androidx.compose.foundation.layout.height
Tff7b72import T7ee787androidx.compose.foundation.layout.heightIn
Tff7b72import T7ee787androidx.compose.foundation.layout.padding
Tff7b72import T7ee787androidx.compose.foundation.layout.size
Tff7b72import T7ee787androidx.compose.material3.Card
Tff7b72import T7ee787androidx.compose.material3.ExperimentalMaterial3Api
Tff7b72import T7ee787androidx.compose.material3.Icon
Tff7b72import T7ee787androidx.compose.material3.MaterialTheme
Tff7b72import T7ee787androidx.compose.material3.Scaffold
Tff7b72import T7ee787androidx.compose.material3.Text
Tff7b72import T7ee787androidx.compose.runtime.Composable
Tff7b72import T7ee787androidx.compose.runtime.LaunchedEffect
Tff7b72import T7ee787androidx.compose.runtime.getValue
Tff7b72import T7ee787androidx.compose.runtime.mutableStateOf
Tff7b72import T7ee787androidx.compose.runtime.remember
Tff7b72import T7ee787androidx.compose.runtime.setValue
Tff7b72import T7ee787androidx.compose.ui.Alignment
Tff7b72import T7ee787androidx.compose.ui.Modifier
Tff7b72import T7ee787androidx.compose.ui.text.style.TextAlign
Tff7b72import T7ee787androidx.compose.ui.unit.dp
Tff7b72import T7ee787androidx.lifecycle.compose.LifecycleStartEffect
Tff7b72import T7ee787androidx.lifecycle.compose.collectAsStateWithLifecycle
Tff7b72import T7ee787org.jetbrains.compose.resources.stringResource
Tff7b72import T7ee787org.koin.compose.viewmodel.koinViewModel
Tff7b72import T7ee787org.meshtastic.core.model.ConnectionState
Tff7b72import T7ee787org.meshtastic.core.model.DeviceType
Tff7b72import T7ee787org.meshtastic.core.model.InterfaceId
Tff7b72import T7ee787org.meshtastic.core.navigation.FirmwareRoute
Tff7b72import T7ee787org.meshtastic.core.navigation.Route
Tff7b72import T7ee787org.meshtastic.core.navigation.SettingsRoute
Tff7b72import T7ee787org.meshtastic.core.resources.Res
Tff7b72import T7ee787org.meshtastic.core.resources.bluetooth_disabled
Tff7b72import T7ee787org.meshtastic.core.resources.connections
Tff7b72import T7ee787org.meshtastic.core.resources.firmware_event_ended_banner
Tff7b72import T7ee787org.meshtastic.core.resources.firmware_event_ended_button
Tff7b72import T7ee787org.meshtastic.core.resources.firmware_recovery_banner
Tff7b72import T7ee787org.meshtastic.core.resources.firmware_recovery_button
Tff7b72import T7ee787org.meshtastic.core.resources.firmware_recovery_dismiss
Tff7b72import T7ee787org.meshtastic.core.resources.no_device_selected
Tff7b72import T7ee787org.meshtastic.core.resources.open_bluetooth_settings
Tff7b72import T7ee787org.meshtastic.core.resources.open_wifi_settings
Tff7b72import T7ee787org.meshtastic.core.resources.set_your_region
Tff7b72import T7ee787org.meshtastic.core.resources.unknown_device
Tff7b72import T7ee787org.meshtastic.core.resources.wifi_unavailable
Tff7b72import T7ee787org.meshtastic.core.ui.component.AdaptiveTwoPane
Tff7b72import T7ee787org.meshtastic.core.ui.component.ListItem
Tff7b72import T7ee787org.meshtastic.core.ui.component.MainAppBar
Tff7b72import T7ee787org.meshtastic.core.ui.component.RecoveryCard
Tff7b72import T7ee787org.meshtastic.core.ui.icon.Bluetooth
Tff7b72import T7ee787org.meshtastic.core.ui.icon.Language
Tff7b72import T7ee787org.meshtastic.core.ui.icon.MeshtasticIcons
Tff7b72import T7ee787org.meshtastic.core.ui.icon.NoDevice
Tff7b72import T7ee787org.meshtastic.core.ui.util.LocalEventBranding
Tff7b72import T7ee787org.meshtastic.core.ui.util.PermissionStatus
Tff7b72import T7ee787org.meshtastic.core.ui.util.hasEnded
Tff7b72import T7ee787org.meshtastic.core.ui.util.isBluetoothDisabled
Tff7b72import T7ee787org.meshtastic.core.ui.util.isWifiUnavailable
Tff7b72import T7ee787org.meshtastic.core.ui.util.rememberBluetoothPermissionState
Tff7b72import T7ee787org.meshtastic.core.ui.util.rememberLocalNetworkPermissionState
Tff7b72import T7ee787org.meshtastic.core.ui.util.rememberOpenBluetoothSettings
Tff7b72import T7ee787org.meshtastic.core.ui.util.rememberOpenWifiSettings
Tff7b72import T7ee787org.meshtastic.core.ui.util.shouldShowWifiUnavailableBanner
Tff7b72import T7ee787org.meshtastic.core.ui.viewmodel.ConnectionStatus
Tff7b72import T7ee787org.meshtastic.core.ui.viewmodel.ConnectionsViewModel
Tff7b72import T7ee787org.meshtastic.feature.connections.NO_DEVICE_SELECTED
Tff7b72import T7ee787org.meshtastic.feature.connections.ScannerViewModel
Tff7b72import T7ee787org.meshtastic.feature.connections.model.DeviceListEntry
Tff7b72import T7ee787org.meshtastic.feature.connections.ui.components.ConnectingDeviceInfo
Tff7b72import T7ee787org.meshtastic.feature.connections.ui.components.CurrentlyConnectedInfo
Tff7b72import T7ee787org.meshtastic.feature.connections.ui.components.DeviceList
Tff7b72import T7ee787org.meshtastic.feature.connections.ui.components.TransportSelector
Tff7b72import T7ee787org.meshtastic.feature.settings.navigation.ConfigRoute
Tff7b72import T7ee787org.meshtastic.feature.settings.navigation.getNavRouteFrom
Tff7b72import T7ee787org.meshtastic.feature.settings.radio.RadioConfigViewModel
Tff7b72import T7ee787org.meshtastic.feature.settings.radio.component.PacketResponseStateDialog

T8b949e/**
* Fixed minimum height for the "connected device" card at the top of the Connections screen. Shared across the three UI
* states (NO_DEVICE, CONNECTING, CONNECTED_WITH_NODE) so the card never collapses or jumps size between state
* transitions. Sized to comfortably fit the CONNECTED state (battery/RSSI row + node row + disconnect button).
*/
Tff7b72private Tff7b72val Te6edf3CardMinHeight Tff7b72= T79c0ff1T79c0ff0T79c0ff0.Te6edf3dp

T8b949e/** Composable screen for managing device connections (BLE, TCP, USB). It displays connection status. */
Tf0883e@OptInTb4b4b4(Te6edf3ExperimentalMaterial3ApiTff7b72::Te6edf3classTb4b4b4)
Tf0883e@SuppressTb4b4b4(Ta5d6ff"Ta5d6ffCyclomaticComplexMethodTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffLongMethodTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffMagicNumberTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffModifierMissingTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffComposableParamOrderTa5d6ff"Tb4b4b4)
Tf0883e@Composable
Tff7b72fun Td2a8ffConnectionsScreenTb4b4b4(
Te6edf3connectionsViewModelTb4b4b4: Te6edf3ConnectionsViewModel Tff7b72= Te6edf3koinViewModelTb4b4b4(Tb4b4b4)Tb4b4b4,
Te6edf3scanModelTb4b4b4: Te6edf3ScannerViewModel Tff7b72= Te6edf3koinViewModelTb4b4b4(Tb4b4b4)Tb4b4b4,
Te6edf3radioConfigViewModelTb4b4b4: Te6edf3RadioConfigViewModel Tff7b72= Te6edf3koinViewModelTb4b4b4(Tb4b4b4)Tb4b4b4,
Te6edf3onClickNodeChipTb4b4b4: Tb4b4b4(Tffa657IntTb4b4b4) Tff7b72-Tff7b72> Tffa657UnitTb4b4b4,
Te6edf3onNavigateToNodeDetailsTb4b4b4: Tb4b4b4(Tffa657IntTb4b4b4) Tff7b72-Tff7b72> Tffa657UnitTb4b4b4,
Te6edf3onConfigNavigateTb4b4b4: Tb4b4b4(Te6edf3RouteTb4b4b4) Tff7b72-Tff7b72> Tffa657UnitTb4b4b4,
Tb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3radioConfigState Tff7b72by Te6edf3radioConfigViewModelTb4b4b4.Te6edf3radioConfigStateTb4b4b4.Te6edf3collectAsStateWithLifecycleTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3connectionProgress Tff7b72by Te6edf3scanModelTb4b4b4.Te6edf3connectionProgressTextTb4b4b4.Te6edf3collectAsStateWithLifecycleTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3connectionStatus Tff7b72by Te6edf3connectionsViewModelTb4b4b4.Te6edf3connectionStatusTb4b4b4.Te6edf3collectAsStateWithLifecycleTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3connectionState Tff7b72by Te6edf3connectionsViewModelTb4b4b4.Te6edf3connectionStateTb4b4b4.Te6edf3collectAsStateWithLifecycleTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3ourNode Tff7b72by Te6edf3connectionsViewModelTb4b4b4.Te6edf3ourNodeForDisplayTb4b4b4.Te6edf3collectAsStateWithLifecycleTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3regionUnset Tff7b72by Te6edf3connectionsViewModelTb4b4b4.Te6edf3regionUnsetTb4b4b4.Te6edf3collectAsStateWithLifecycleTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3sessionAuthorized Tff7b72by Te6edf3connectionsViewModelTb4b4b4.Te6edf3sessionAuthorizedTb4b4b4.Te6edf3collectAsStateWithLifecycleTb4b4b4(Tb4b4b4)

Tff7b72val Te6edf3selectedDevice Tff7b72by Te6edf3scanModelTb4b4b4.Te6edf3selectedNotNullFlowTb4b4b4.Te6edf3collectAsStateWithLifecycleTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3persistedDeviceName Tff7b72by Te6edf3scanModelTb4b4b4.Te6edf3persistedDeviceNameTb4b4b4.Te6edf3collectAsStateWithLifecycleTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3pendingRecovery Tff7b72by Te6edf3scanModelTb4b4b4.Te6edf3pendingRecoveryTb4b4b4.Te6edf3collectAsStateWithLifecycleTb4b4b4(Tb4b4b4)

Tff7b72val Te6edf3bleDevices Tff7b72by Te6edf3scanModelTb4b4b4.Te6edf3bleDevicesForUiTb4b4b4.Te6edf3collectAsStateWithLifecycleTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3discoveredTcpDevices Tff7b72by Te6edf3scanModelTb4b4b4.Te6edf3discoveredTcpDevicesForUiTb4b4b4.Te6edf3collectAsStateWithLifecycleTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3recentTcpDevices Tff7b72by Te6edf3scanModelTb4b4b4.Te6edf3recentTcpDevicesForUiTb4b4b4.Te6edf3collectAsStateWithLifecycleTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3usbDevices Tff7b72by Te6edf3scanModelTb4b4b4.Te6edf3usbDevicesForUiTb4b4b4.Te6edf3collectAsStateWithLifecycleTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3isBleScanning Tff7b72by Te6edf3scanModelTb4b4b4.Te6edf3isBleScanningTb4b4b4.Te6edf3collectAsStateWithLifecycleTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3isNetworkScanning Tff7b72by Te6edf3scanModelTb4b4b4.Te6edf3isNetworkScanningTb4b4b4.Te6edf3collectAsStateWithLifecycleTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3activeTransport Tff7b72by Te6edf3scanModelTb4b4b4.Te6edf3activeTransportTb4b4b4.Te6edf3collectAsStateWithLifecycleTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3bleAutoScan Tff7b72by Te6edf3scanModelTb4b4b4.Te6edf3bleAutoScanTb4b4b4.Te6edf3collectAsStateWithLifecycleTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3networkAutoScan Tff7b72by Te6edf3scanModelTb4b4b4.Te6edf3networkAutoScanTb4b4b4.Te6edf3collectAsStateWithLifecycleTb4b4b4(Tb4b4b4)

T8b949e// Android 17 (API 37) gates NSD/mDNS behind ACCESS_LOCAL_NETWORK. Without this prompt the platform falls back to
T8b949e// the system "Choose a device to connect" picker on every discoverServices() call. The reactive state lets the
T8b949e// network-scan toggle request in-context and route a permanent denial to settings.
Tff7b72val Te6edf3localNetworkPermission Tff7b72= Te6edf3rememberLocalNetworkPermissionStateTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3bluetoothPermission Tff7b72= Te6edf3rememberBluetoothPermissionStateTb4b4b4(Tb4b4b4)

T8b949e// Adapter-state, distinct from permission state: a permission can be granted while Bluetooth is off or the device
T8b949e// is off Wi-Fi. Detected separately so the UI can route to the adapter's settings rather than re-prompting.
Tff7b72val Te6edf3bluetoothDisabled Tff7b72= Te6edf3isBluetoothDisabledTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3wifiUnavailable Tff7b72= Te6edf3isWifiUnavailableTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3openBluetoothSettings Tff7b72= Te6edf3rememberOpenBluetoothSettingsTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3openWifiSettings Tff7b72= Te6edf3rememberOpenWifiSettingsTb4b4b4(Tb4b4b4)

T8b949e// Auto-start BLE discovery when the screen is visible (lifecycle β‰₯ STARTED) and the user has previously opted in.
T8b949e// ScannerViewModel skips screen-entry discovery when a selected device can reconnect through the transport's
T8b949e// fresh-advertisement scan. LifecycleStartEffect stops scanning on ON_STOP (app backgrounded) and restarts on
T8b949e// ON_START β€” preventing continuous background BLE radio usage that drains the battery.
T8b949e// Keyed on the active pane so a persisted TCP/USB pane loaded after first composition disposes this effect and
T8b949e// stops an initially eligible BLE scan. The toggle handler starts/stops scans directly; this effect owns lifecycle
T8b949e// cleanup, while the LaunchedEffect below handles async preference loading without disposing the lifecycle owner.
Te6edf3LifecycleStartEffectTb4b4b4(Te6edf3activeTransportTb4b4b4) Tb4b4b4{
Tff7b72if Tb4b4b4(Te6edf3activeTransport Tff7b72=Tff7b72= Te6edf3DeviceTypeTb4b4b4.Te6edf3BLE Tff7b72&Tff7b72& Te6edf3bleAutoScan Tff7b72&Tff7b72& Tff7b72!Te6edf3isBleScanningTb4b4b4) Tb4b4b4{
Te6edf3scanModelTb4b4b4.Te6edf3startBleAutoScanTb4b4b4(Tb4b4b4)
Tb4b4b4}
Te6edf3onStopOrDispose Tb4b4b4{ Te6edf3scanModelTb4b4b4.Te6edf3stopBleScanTb4b4b4(Tb4b4b4) Tb4b4b4}
Tb4b4b4}

Te6edf3LaunchedEffectTb4b4b4(Te6edf3activeTransportTb4b4b4, Te6edf3bleAutoScanTb4b4b4) Tb4b4b4{
Tff7b72if Tb4b4b4(Te6edf3activeTransport Tff7b72=Tff7b72= Te6edf3DeviceTypeTb4b4b4.Te6edf3BLE Tff7b72&Tff7b72& Te6edf3bleAutoScan Tff7b72&Tff7b72& Tff7b72!Te6edf3isBleScanningTb4b4b4) Tb4b4b4{
Te6edf3scanModelTb4b4b4.Te6edf3startBleAutoScanTb4b4b4(Tb4b4b4)
Tb4b4b4}
Tb4b4b4}

T8b949e// Keyed on active pane and permission status so the lifecycle owner re-fires when the user grants local-network
T8b949e// permission or the persisted Network pane loads. The separate LaunchedEffect handles later pref arrival without a
T8b949e// dispose+restart cycle on manual scan preference writes.
Te6edf3LifecycleStartEffectTb4b4b4(Te6edf3activeTransportTb4b4b4, Te6edf3localNetworkPermissionTb4b4b4.Te6edf3isGrantedTb4b4b4) Tb4b4b4{
Tff7b72if Tb4b4b4(
Te6edf3activeTransport Tff7b72=Tff7b72= Te6edf3DeviceTypeTb4b4b4.Te6edf3TCP Tff7b72&Tff7b72&
Te6edf3networkAutoScan Tff7b72&Tff7b72&
Te6edf3localNetworkPermissionTb4b4b4.Te6edf3isGranted Tff7b72&Tff7b72&
Tff7b72!Te6edf3isNetworkScanning
Tb4b4b4) Tb4b4b4{
Te6edf3scanModelTb4b4b4.Te6edf3startNetworkAutoScanTb4b4b4(Tb4b4b4)
Tb4b4b4}
Te6edf3onStopOrDispose Tb4b4b4{ Te6edf3scanModelTb4b4b4.Te6edf3stopNetworkScanTb4b4b4(Tb4b4b4) Tb4b4b4}
Tb4b4b4}

Te6edf3LaunchedEffectTb4b4b4(Te6edf3activeTransportTb4b4b4, Te6edf3localNetworkPermissionTb4b4b4.Te6edf3isGrantedTb4b4b4, Te6edf3networkAutoScanTb4b4b4) Tb4b4b4{
Tff7b72if Tb4b4b4(
Te6edf3activeTransport Tff7b72=Tff7b72= Te6edf3DeviceTypeTb4b4b4.Te6edf3TCP Tff7b72&Tff7b72&
Te6edf3networkAutoScan Tff7b72&Tff7b72&
Te6edf3localNetworkPermissionTb4b4b4.Te6edf3isGranted Tff7b72&Tff7b72&
Tff7b72!Te6edf3isNetworkScanning
Tb4b4b4) Tb4b4b4{
Te6edf3scanModelTb4b4b4.Te6edf3startNetworkAutoScanTb4b4b4(Tb4b4b4)
Tb4b4b4}
Tb4b4b4}

T8b949e/* Animate waiting for the configurations */
Tff7b72var Te6edf3isWaiting Tff7b72by Te6edf3remember Tb4b4b4{ Te6edf3mutableStateOfTb4b4b4(Tff7b72falseTb4b4b4) Tb4b4b4}
Tff7b72if Tb4b4b4(Te6edf3isWaitingTb4b4b4) Tb4b4b4{
Te6edf3PacketResponseStateDialogTb4b4b4(
Te6edf3state Tff7b72= Te6edf3radioConfigStateTb4b4b4.Te6edf3responseStateTb4b4b4,
Te6edf3onDismiss Tff7b72= Tb4b4b4{
Te6edf3isWaiting Tff7b72= Tff7b72false
Te6edf3radioConfigViewModelTb4b4b4.Te6edf3clearPacketResponseTb4b4b4(Tb4b4b4)
Tb4b4b4}Tb4b4b4,
Te6edf3onComplete Tff7b72= Tb4b4b4{
Te6edf3getNavRouteFromTb4b4b4(Te6edf3radioConfigStateTb4b4b4.Te6edf3routeTb4b4b4)Tff7b72?.Te6edf3let Tb4b4b4{ Te6edf3route Tff7b72-Tff7b72>
Te6edf3isWaiting Tff7b72= Tff7b72false
Te6edf3radioConfigViewModelTb4b4b4.Te6edf3clearPacketResponseTb4b4b4(Tb4b4b4)
Tff7b72if Tb4b4b4(Te6edf3route Tff7b72=Tff7b72= Te6edf3SettingsRouteTb4b4b4.Te6edf3LoRaTb4b4b4) Tb4b4b4{
Te6edf3onConfigNavigateTb4b4b4(Te6edf3SettingsRouteTb4b4b4.Te6edf3LoRaTb4b4b4)
Tb4b4b4}
Tb4b4b4}
Tb4b4b4}Tb4b4b4,
Tb4b4b4)
Tb4b4b4}

Te6edf3ScaffoldTb4b4b4(
Te6edf3topBar Tff7b72= Tb4b4b4{
Te6edf3MainAppBarTb4b4b4(
Te6edf3title Tff7b72= Te6edf3stringResourceTb4b4b4(Te6edf3ResTb4b4b4.Te6edf3stringTb4b4b4.Te6edf3connectionsTb4b4b4)Tb4b4b4,
Te6edf3ourNode Tff7b72= Te6edf3ourNodeTb4b4b4,
Te6edf3showNodeChip Tff7b72= Te6edf3ourNode Tff7b72!Tff7b72= Tff7b72null Tff7b72&Tff7b72& Te6edf3connectionState Tff7b72is Te6edf3ConnectionStateTb4b4b4.Te6edf3ConnectedTb4b4b4,
Te6edf3canNavigateUp Tff7b72= Tff7b72falseTb4b4b4,
Te6edf3onNavigateUp Tff7b72= Tb4b4b4{Tb4b4b4}Tb4b4b4,
Te6edf3actions Tff7b72= Tb4b4b4{Tb4b4b4}Tb4b4b4,
Te6edf3onClickChip Tff7b72= Tb4b4b4{ Te6edf3onClickNodeChipTb4b4b4(Tffa657itTb4b4b4.Te6edf3numTb4b4b4) Tb4b4b4}Tb4b4b4,
Tb4b4b4)
Tb4b4b4}Tb4b4b4,
Tb4b4b4) Tb4b4b4{ Te6edf3paddingValues Tff7b72-Tff7b72>
Te6edf3BoxTb4b4b4(Te6edf3modifier Tff7b72= Te6edf3ModifierTb4b4b4.Te6edf3fillMaxSizeTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4{
Te6edf3ColumnTb4b4b4(
Te6edf3modifier Tff7b72= Te6edf3ModifierTb4b4b4.Te6edf3fillMaxSizeTb4b4b4(Tb4b4b4)Tb4b4b4.Te6edf3paddingTb4b4b4(Te6edf3paddingValuesTb4b4b4)Tb4b4b4.Te6edf3paddingTb4b4b4(Te6edf3horizontal Tff7b72= T79c0ff1T79c0ff6.Te6edf3dpTb4b4b4)Tb4b4b4,
Te6edf3verticalArrangement Tff7b72= Te6edf3ArrangementTb4b4b4.Te6edf3spacedByTb4b4b4(T79c0ff1T79c0ff6.Te6edf3dpTb4b4b4)Tb4b4b4,
Tb4b4b4) Tb4b4b4{
Te6edf3SpacerTb4b4b4(Te6edf3modifier Tff7b72= Te6edf3ModifierTb4b4b4.Te6edf3heightTb4b4b4(T79c0ff4.Te6edf3dpTb4b4b4)Tb4b4b4)

Te6edf3AdaptiveTwoPaneTb4b4b4(
Te6edf3first Tff7b72= Tb4b4b4{
Tff7b72val Te6edf3uiState Tff7b72=
Tff7b72when Tb4b4b4{
Te6edf3connectionState Tff7b72is Te6edf3ConnectionStateTb4b4b4.Te6edf3Connected Tff7b72&Tff7b72& Te6edf3ourNode Tff7b72!Tff7b72= Tff7b72null Tff7b72-Tff7b72>
Te6edf3ConnectionUiStateTb4b4b4.Te6edf3CONNECTED_WITH_NODE

Te6edf3connectionState Tff7b72is Te6edf3ConnectionStateTb4b4b4.Te6edf3Connected Tff7b72|Tff7b72|
Te6edf3connectionState Tff7b72=Tff7b72= Te6edf3ConnectionStateTb4b4b4.Te6edf3Connecting Tff7b72|Tff7b72|
Te6edf3selectedDevice Tff7b72!Tff7b72= Te6edf3NO_DEVICE_SELECTED Tff7b72-Tff7b72> Te6edf3ConnectionUiStateTb4b4b4.Te6edf3CONNECTING

Tff7b72else Tff7b72-Tff7b72> Te6edf3ConnectionUiStateTb4b4b4.Te6edf3NO_DEVICE
Tb4b4b4}

T8b949e// ── Connected Device slot ──
T8b949e// A single Card shell hosts all three states. `animateContentSize` smooths any
T8b949e// height changes, while `heightIn(min = CardMinHeight)` reserves a stable floor so
T8b949e// the card never collapses between states.
Te6edf3CardTb4b4b4(Te6edf3modifier Tff7b72= Te6edf3ModifierTb4b4b4.Te6edf3fillMaxWidthTb4b4b4(Tb4b4b4)Tb4b4b4.Te6edf3animateContentSizeTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4{
Te6edf3AnimatedContentTb4b4b4(
Te6edf3targetState Tff7b72= Te6edf3uiStateTb4b4b4,
Te6edf3label Tff7b72= Ta5d6ff"Ta5d6ffconnection_stateTa5d6ff"Tb4b4b4,
Te6edf3transitionSpec Tff7b72= Tb4b4b4{ Te6edf3fadeInTb4b4b4(Tb4b4b4) Te6edf3togetherWith Te6edf3fadeOutTb4b4b4(Tb4b4b4) Tb4b4b4}Tb4b4b4,
Te6edf3modifier Tff7b72= Te6edf3ModifierTb4b4b4.Te6edf3fillMaxWidthTb4b4b4(Tb4b4b4)Tb4b4b4,
Tb4b4b4) Tb4b4b4{ Te6edf3state Tff7b72-Tff7b72>
Te6edf3BoxTb4b4b4(
Te6edf3modifier Tff7b72= Te6edf3ModifierTb4b4b4.Te6edf3fillMaxWidthTb4b4b4(Tb4b4b4)Tb4b4b4.Te6edf3heightInTb4b4b4(Te6edf3min Tff7b72= Te6edf3CardMinHeightTb4b4b4)Tb4b4b4,
Te6edf3contentAlignment Tff7b72= Te6edf3AlignmentTb4b4b4.Te6edf3CenterTb4b4b4,
Tb4b4b4) Tb4b4b4{
Tff7b72when Tb4b4b4(Te6edf3stateTb4b4b4) Tb4b4b4{
Te6edf3ConnectionUiStateTb4b4b4.Te6edf3CONNECTED_WITH_NODE Tff7b72-Tff7b72>
Te6edf3ConnectedDeviceContentTb4b4b4(
Te6edf3ourNode Tff7b72= Te6edf3ourNodeTb4b4b4,
Te6edf3selectedDevice Tff7b72= Te6edf3selectedDeviceTb4b4b4,
Te6edf3bleDevices Tff7b72= Te6edf3bleDevicesTb4b4b4,
Te6edf3onNavigateToNodeDetails Tff7b72= Te6edf3onNavigateToNodeDetailsTb4b4b4,
Te6edf3onClickDisconnect Tff7b72= Tb4b4b4{ Te6edf3scanModelTb4b4b4.Te6edf3disconnectTb4b4b4(Tb4b4b4) Tb4b4b4}Tb4b4b4,
Tb4b4b4)

Te6edf3ConnectionUiStateTb4b4b4.Te6edf3CONNECTING Tff7b72-Tff7b72>
Te6edf3ConnectingDeviceContentTb4b4b4(
Te6edf3selectedDevice Tff7b72= Te6edf3selectedDeviceTb4b4b4,
Te6edf3persistedDeviceName Tff7b72= Te6edf3persistedDeviceNameTb4b4b4,
Te6edf3bleDevices Tff7b72= Te6edf3bleDevicesTb4b4b4,
Te6edf3discoveredTcpDevices Tff7b72= Te6edf3discoveredTcpDevicesTb4b4b4,
Te6edf3recentTcpDevices Tff7b72= Te6edf3recentTcpDevicesTb4b4b4,
Te6edf3usbDevices Tff7b72= Te6edf3usbDevicesTb4b4b4,
Te6edf3connectionStatus Tff7b72= Te6edf3connectionStatusTb4b4b4,
Te6edf3connectionProgress Tff7b72= Te6edf3connectionProgressTb4b4b4,
Te6edf3onClickDisconnect Tff7b72= Tb4b4b4{ Te6edf3scanModelTb4b4b4.Te6edf3disconnectTb4b4b4(Tb4b4b4) Tb4b4b4}Tb4b4b4,
Tb4b4b4)

Tff7b72else Tff7b72-Tff7b72> Te6edf3NoDeviceContentTb4b4b4(Tb4b4b4)
Tb4b4b4}
Tb4b4b4}
Tb4b4b4}
Tb4b4b4}

T8b949e// A device stranded in bootloader mode by an interrupted update can be re-flashed without
T8b949e// reconnecting first. Shown only while disconnected so the Firmware screen enters its recovery
T8b949e// path (it uses the live connection when connected); cleared automatically once the device
T8b949e// returns on its own.
Te6edf3pendingRecovery
Tff7b72?.Te6edf3takeIf Tb4b4b4{ Te6edf3connectionState Tff7b72!is Te6edf3ConnectionStateTb4b4b4.Te6edf3Connected Tb4b4b4}
Tff7b72?.Te6edf3let Tb4b4b4{ Te6edf3recovery Tff7b72-Tff7b72>
Te6edf3SpacerTb4b4b4(Te6edf3modifier Tff7b72= Te6edf3ModifierTb4b4b4.Te6edf3heightTb4b4b4(T79c0ff8.Te6edf3dpTb4b4b4)Tb4b4b4)
Te6edf3RecoveryCardTb4b4b4(
Te6edf3message Tff7b72= Te6edf3stringResourceTb4b4b4(Te6edf3ResTb4b4b4.Te6edf3stringTb4b4b4.Te6edf3firmware_recovery_bannerTb4b4b4, Te6edf3recoveryTb4b4b4.Te6edf3deviceNameTb4b4b4)Tb4b4b4,
Te6edf3actionLabel Tff7b72= Te6edf3stringResourceTb4b4b4(Te6edf3ResTb4b4b4.Te6edf3stringTb4b4b4.Te6edf3firmware_recovery_buttonTb4b4b4)Tb4b4b4,
Te6edf3onAction Tff7b72= Tb4b4b4{ Te6edf3onConfigNavigateTb4b4b4(Te6edf3FirmwareRouteTb4b4b4.Te6edf3FirmwareUpdateTb4b4b4) Tb4b4b4}Tb4b4b4,
Te6edf3actionIcon Tff7b72= Te6edf3MeshtasticIconsTb4b4b4.Te6edf3BluetoothTb4b4b4,
T8b949e// Let the user dismiss a recovery that can't succeed (e.g. an unflashable stock
T8b949e// bootloader) so it doesn't nag forever; it otherwise only clears on
T8b949e// reconnect/success.
Te6edf3onDismiss Tff7b72= Tb4b4b4{ Te6edf3scanModelTb4b4b4.Te6edf3dismissRecoveryTb4b4b4(Tb4b4b4) Tb4b4b4}Tb4b4b4,
Te6edf3dismissContentDescription Tff7b72= Te6edf3stringResourceTb4b4b4(Te6edf3ResTb4b4b4.Te6edf3stringTb4b4b4.Te6edf3firmware_recovery_dismissTb4b4b4)Tb4b4b4,
Tb4b4b4)
Tb4b4b4}

T8b949e// Once an event is over, nudge users still on that event's firmware back to standard
T8b949e// firmware. Driven purely by the metadata end date (LocalEventBranding is only populated
T8b949e// while connected to event firmware), so it appears whenever an ended-event device is
T8b949e// connected and disappears on its own once the device is re-flashed to vanilla. Not
T8b949e// dismissable β€” it stays until the underlying condition is actually resolved.
Te6edf3LocalEventBrandingTb4b4b4.Te6edf3current
Tff7b72?.Te6edf3takeIf Tb4b4b4{ Tffa657itTb4b4b4.Te6edf3hasEndedTb4b4b4(Tb4b4b4) Tb4b4b4}
Tff7b72?.Te6edf3let Tb4b4b4{ Te6edf3endedEvent Tff7b72-Tff7b72>
Te6edf3SpacerTb4b4b4(Te6edf3modifier Tff7b72= Te6edf3ModifierTb4b4b4.Te6edf3heightTb4b4b4(T79c0ff8.Te6edf3dpTb4b4b4)Tb4b4b4)
Te6edf3RecoveryCardTb4b4b4(
Te6edf3message Tff7b72=
Te6edf3stringResourceTb4b4b4(Te6edf3ResTb4b4b4.Te6edf3stringTb4b4b4.Te6edf3firmware_event_ended_bannerTb4b4b4, Te6edf3endedEventTb4b4b4.Te6edf3displayNameTb4b4b4)Tb4b4b4,
Te6edf3actionLabel Tff7b72= Te6edf3stringResourceTb4b4b4(Te6edf3ResTb4b4b4.Te6edf3stringTb4b4b4.Te6edf3firmware_event_ended_buttonTb4b4b4)Tb4b4b4,
Te6edf3onAction Tff7b72= Tb4b4b4{ Te6edf3onConfigNavigateTb4b4b4(Te6edf3FirmwareRouteTb4b4b4.Te6edf3FirmwareUpdateTb4b4b4) Tb4b4b4}Tb4b4b4,
Tb4b4b4)
Tb4b4b4}

T8b949e// Region warning sits outside the animated card so it does not affect the
T8b949e// CONNECTED ↔ CONNECTING ↔ NO_DEVICE size transition.
Tff7b72val Te6edf3isPhysicalDevice Tff7b72=
Te6edf3selectedDevice Tff7b72!Tff7b72= Te6edf3InterfaceIdTb4b4b4.Te6edf3MOCKTb4b4b4.Te6edf3idTb4b4b4.Te6edf3toStringTb4b4b4(Tb4b4b4) Tff7b72&Tff7b72&
Te6edf3selectedDevice Tff7b72!Tff7b72= Te6edf3InterfaceIdTb4b4b4.Te6edf3REPLAYTb4b4b4.Te6edf3idTb4b4b4.Te6edf3toStringTb4b4b4(Tb4b4b4)
Tff7b72if Tb4b4b4(
Te6edf3uiState Tff7b72=Tff7b72= Te6edf3ConnectionUiStateTb4b4b4.Te6edf3CONNECTED_WITH_NODE Tff7b72&Tff7b72&
Te6edf3regionUnset Tff7b72&Tff7b72&
Te6edf3sessionAuthorized Tff7b72&Tff7b72&
Te6edf3isPhysicalDevice
Tb4b4b4) Tb4b4b4{
Te6edf3SpacerTb4b4b4(Te6edf3modifier Tff7b72= Te6edf3ModifierTb4b4b4.Te6edf3heightTb4b4b4(T79c0ff8.Te6edf3dpTb4b4b4)Tb4b4b4)
Te6edf3CardTb4b4b4(Te6edf3modifier Tff7b72= Te6edf3ModifierTb4b4b4.Te6edf3fillMaxWidthTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4{
Te6edf3ListItemTb4b4b4(
Te6edf3leadingIcon Tff7b72= Te6edf3MeshtasticIconsTb4b4b4.Te6edf3LanguageTb4b4b4,
Te6edf3text Tff7b72= Te6edf3stringResourceTb4b4b4(Te6edf3ResTb4b4b4.Te6edf3stringTb4b4b4.Te6edf3set_your_regionTb4b4b4)Tb4b4b4,
Te6edf3onClick Tff7b72= Tb4b4b4{
Te6edf3isWaiting Tff7b72= Tff7b72true
Te6edf3radioConfigViewModelTb4b4b4.Te6edf3setResponseStateLoadingTb4b4b4(Te6edf3ConfigRouteTb4b4b4.Te6edf3LORATb4b4b4)
Tb4b4b4}Tb4b4b4,
Tb4b4b4)
Tb4b4b4}
Tb4b4b4}

T8b949e// Transport selector sits between the connection card and device list; it controls only the
T8b949e// visible discovery pane, not the globally selected/connected device shown above.
Te6edf3TransportSelectorTb4b4b4(
Te6edf3activeTransport Tff7b72= Te6edf3activeTransportTb4b4b4,
Te6edf3onSelectTransport Tff7b72= Te6edf3scanModelTff7b72::Te6edf3selectTransportTb4b4b4,
Tb4b4b4)

T8b949e// Adapter-off hints: shown only when the relevant permission is granted but the radio/network
T8b949e// is unavailable, so they don't overlap the permission-recovery flow on the scan toggles.
T8b949e// The WiFi-unavailable banner only renders while a network scan is actively running β€”
T8b949e// discovery is the only moment the user needs to know WiFi is missing. The auto-scan case is
T8b949e// covered because `isNetworkScanning` is true during auto-scan regardless of pane state.
Tff7b72if Tb4b4b4(Te6edf3activeTransport Tff7b72=Tff7b72= Te6edf3DeviceTypeTb4b4b4.Te6edf3BLE Tff7b72&Tff7b72& Te6edf3bluetoothPermissionTb4b4b4.Te6edf3isGranted Tff7b72&Tff7b72& Te6edf3bluetoothDisabledTb4b4b4) Tb4b4b4{
Te6edf3RecoveryCardTb4b4b4(
Te6edf3message Tff7b72= Te6edf3stringResourceTb4b4b4(Te6edf3ResTb4b4b4.Te6edf3stringTb4b4b4.Te6edf3bluetooth_disabledTb4b4b4)Tb4b4b4,
Te6edf3actionLabel Tff7b72= Te6edf3stringResourceTb4b4b4(Te6edf3ResTb4b4b4.Te6edf3stringTb4b4b4.Te6edf3open_bluetooth_settingsTb4b4b4)Tb4b4b4,
Te6edf3onAction Tff7b72= Te6edf3openBluetoothSettingsTb4b4b4,
Te6edf3actionIcon Tff7b72= Te6edf3MeshtasticIconsTb4b4b4.Te6edf3BluetoothTb4b4b4,
Tb4b4b4)
Tb4b4b4}
Tff7b72if Tb4b4b4(
Te6edf3activeTransport Tff7b72=Tff7b72= Te6edf3DeviceTypeTb4b4b4.Te6edf3TCP Tff7b72&Tff7b72&
Te6edf3shouldShowWifiUnavailableBannerTb4b4b4(
Te6edf3isNetworkScanning Tff7b72= Te6edf3isNetworkScanningTb4b4b4,
Te6edf3localNetworkPermissionGranted Tff7b72= Te6edf3localNetworkPermissionTb4b4b4.Te6edf3isGrantedTb4b4b4,
Te6edf3wifiUnavailable Tff7b72= Te6edf3wifiUnavailableTb4b4b4,
Te6edf3discoveredTcpDevicesEmpty Tff7b72= Te6edf3discoveredTcpDevicesTb4b4b4.Te6edf3isEmptyTb4b4b4(Tb4b4b4)Tb4b4b4,
Tb4b4b4)
Tb4b4b4) Tb4b4b4{
Te6edf3RecoveryCardTb4b4b4(
Te6edf3message Tff7b72= Te6edf3stringResourceTb4b4b4(Te6edf3ResTb4b4b4.Te6edf3stringTb4b4b4.Te6edf3wifi_unavailableTb4b4b4)Tb4b4b4,
Te6edf3actionLabel Tff7b72= Te6edf3stringResourceTb4b4b4(Te6edf3ResTb4b4b4.Te6edf3stringTb4b4b4.Te6edf3open_wifi_settingsTb4b4b4)Tb4b4b4,
Te6edf3onAction Tff7b72= Te6edf3openWifiSettingsTb4b4b4,
Tb4b4b4)
Tb4b4b4}
Tb4b4b4}Tb4b4b4,
Te6edf3second Tff7b72= Tb4b4b4{
T8b949e// ── Unified device list ──
Te6edf3BoxTb4b4b4(Te6edf3modifier Tff7b72= Te6edf3ModifierTb4b4b4.Te6edf3weightTb4b4b4(T79c0ff1fTb4b4b4)Tb4b4b4.Te6edf3fillMaxWidthTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4{
Te6edf3DeviceListTb4b4b4(
Te6edf3connectionState Tff7b72= Te6edf3connectionStateTb4b4b4,
Te6edf3selectedDevice Tff7b72= Te6edf3selectedDeviceTb4b4b4,
Te6edf3bleDevices Tff7b72= Te6edf3bleDevicesTb4b4b4,
Te6edf3usbDevices Tff7b72= Te6edf3usbDevicesTb4b4b4,
Te6edf3discoveredTcpDevices Tff7b72= Te6edf3discoveredTcpDevicesTb4b4b4,
Te6edf3recentTcpDevices Tff7b72= Te6edf3recentTcpDevicesTb4b4b4,
Te6edf3isBleScanning Tff7b72= Te6edf3isBleScanningTb4b4b4,
Te6edf3isNetworkScanning Tff7b72= Te6edf3isNetworkScanningTb4b4b4,
Te6edf3activeTransport Tff7b72= Te6edf3activeTransportTb4b4b4,
Te6edf3onSelectDevice Tff7b72= Tb4b4b4{ Te6edf3scanModelTb4b4b4.Te6edf3onSelectedTb4b4b4(Tffa657itTb4b4b4) Tb4b4b4}Tb4b4b4,
Te6edf3onToggleBleScan Tff7b72= Tb4b4b4{
Tff7b72when Tb4b4b4{
T8b949e// Always allow stopping an in-progress scan.
Te6edf3isBleScanning Tff7b72-Tff7b72> Te6edf3scanModelTb4b4b4.Te6edf3toggleBleScanTb4b4b4(Tb4b4b4)

T8b949e// Granted but the radio is off β€” scanning can't work, so open BT settings.
Te6edf3bluetoothPermissionTb4b4b4.Te6edf3isGranted Tff7b72&Tff7b72& Te6edf3bluetoothDisabled Tff7b72-Tff7b72> Te6edf3openBluetoothSettingsTb4b4b4(Tb4b4b4)

Te6edf3bluetoothPermissionTb4b4b4.Te6edf3isGranted Tff7b72-Tff7b72> Te6edf3scanModelTb4b4b4.Te6edf3toggleBleScanTb4b4b4(Tb4b4b4)

T8b949e// Permanently denied: the system won't prompt again, so send to settings.
Te6edf3bluetoothPermissionTb4b4b4.Te6edf3status Tff7b72=Tff7b72= Te6edf3PermissionStatusTb4b4b4.Te6edf3PERMANENTLY_DENIED Tff7b72-Tff7b72>
Te6edf3bluetoothPermissionTb4b4b4.Te6edf3openAppSettingsTb4b4b4(Tb4b4b4)

T8b949e// Request in-context; once granted the user can start scanning.
Tff7b72else Tff7b72-Tff7b72> Te6edf3bluetoothPermissionTb4b4b4.Te6edf3requestTb4b4b4(Tb4b4b4)
Tb4b4b4}
Tb4b4b4}Tb4b4b4,
Te6edf3onToggleNetworkScan Tff7b72= Tb4b4b4{
Tff7b72when Tb4b4b4{
Te6edf3isNetworkScanning Tff7b72|Tff7b72| Te6edf3localNetworkPermissionTb4b4b4.Te6edf3isGranted Tff7b72-Tff7b72>
Te6edf3scanModelTb4b4b4.Te6edf3toggleNetworkScanTb4b4b4(Tb4b4b4)

Te6edf3localNetworkPermissionTb4b4b4.Te6edf3status Tff7b72=Tff7b72= Te6edf3PermissionStatusTb4b4b4.Te6edf3PERMANENTLY_DENIED Tff7b72-Tff7b72>
Te6edf3localNetworkPermissionTb4b4b4.Te6edf3openAppSettingsTb4b4b4(Tb4b4b4)

Tff7b72else Tff7b72-Tff7b72> Tb4b4b4{
T8b949e// Prefer requesting the runtime grant over letting the platform fall back
T8b949e// to the system NSD picker. Persist the user's intent so that if they
T8b949e// grant after the prompt, the scan starts via the LifecycleStartEffect and
T8b949e// stays on for next session.
Te6edf3scanModelTb4b4b4.Te6edf3persistNetworkAutoScanIntentTb4b4b4(Tff7b72trueTb4b4b4)
Te6edf3localNetworkPermissionTb4b4b4.Te6edf3requestTb4b4b4(Tb4b4b4)
Tb4b4b4}
Tb4b4b4}
Tb4b4b4}Tb4b4b4,
Te6edf3onAddManualAddress Tff7b72= Tb4b4b4{ Te6edf3_Tb4b4b4, Te6edf3fullAddress Tff7b72-Tff7b72>
Te6edf3scanModelTb4b4b4.Te6edf3connectToManualAddressTb4b4b4(Te6edf3fullAddressTb4b4b4)
Tb4b4b4}Tb4b4b4,
Te6edf3onRemoveRecentAddress Tff7b72= Tb4b4b4{ Te6edf3scanModelTb4b4b4.Te6edf3removeRecentAddressTb4b4b4(Tffa657itTb4b4b4.Te6edf3fullAddressTb4b4b4) Tb4b4b4}Tb4b4b4,
Tb4b4b4)
Tb4b4b4}
Tb4b4b4}Tb4b4b4,
Tb4b4b4)
Tb4b4b4}
Tb4b4b4}
Tb4b4b4}
Tb4b4b4}

T8b949e/** Body for the CONNECTED state β€” sits inside the shared outer Card in [ConnectionsScreen]. */
Tf0883e@Composable
Tff7b72private Tff7b72fun Td2a8ffConnectedDeviceContentTb4b4b4(
Te6edf3ourNodeTb4b4b4: Te6edf3orgTb4b4b4.Te6edf3meshtasticTb4b4b4.Te6edf3coreTb4b4b4.Te6edf3modelTb4b4b4.Te6edf3NodeTff7b72?Tb4b4b4,
Te6edf3selectedDeviceTb4b4b4: Tffa657StringTb4b4b4,
Te6edf3bleDevicesTb4b4b4: Te6edf3ListTff7b72<Te6edf3DeviceListEntryTff7b72>Tb4b4b4,
Te6edf3onNavigateToNodeDetailsTb4b4b4: Tb4b4b4(Tffa657IntTb4b4b4) Tff7b72-Tff7b72> Tffa657UnitTb4b4b4,
Te6edf3onClickDisconnectTb4b4b4: Tb4b4b4(Tb4b4b4) Tff7b72-Tff7b72> Tffa657UnitTb4b4b4,
Tb4b4b4) Tb4b4b4{
Te6edf3ourNodeTff7b72?.Te6edf3let Tb4b4b4{ Te6edf3node Tff7b72-Tff7b72>
Te6edf3CurrentlyConnectedInfoTb4b4b4(
Te6edf3node Tff7b72= Te6edf3nodeTb4b4b4,
Te6edf3bleDevice Tff7b72= Te6edf3bleDevicesTb4b4b4.Te6edf3find Tb4b4b4{ Tffa657itTb4b4b4.Te6edf3fullAddress Tff7b72=Tff7b72= Te6edf3selectedDevice Tb4b4b4} Tff7b72as Te6edf3DeviceListEntryTb4b4b4.Te6edf3BleTff7b72?Tb4b4b4,
Te6edf3onNavigateToNodeDetails Tff7b72= Te6edf3onNavigateToNodeDetailsTb4b4b4,
Te6edf3onClickDisconnect Tff7b72= Te6edf3onClickDisconnectTb4b4b4,
Tb4b4b4)
Tb4b4b4}
Tb4b4b4}

T8b949e/** Body for the CONNECTING state β€” sits inside the shared outer Card in [ConnectionsScreen]. */
Tf0883e@Composable
Tff7b72private Tff7b72fun Td2a8ffConnectingDeviceContentTb4b4b4(
Te6edf3selectedDeviceTb4b4b4: Tffa657StringTb4b4b4,
Te6edf3persistedDeviceNameTb4b4b4: Tffa657String?Tb4b4b4,
Te6edf3bleDevicesTb4b4b4: Te6edf3ListTff7b72<Te6edf3DeviceListEntryTff7b72>Tb4b4b4,
Te6edf3discoveredTcpDevicesTb4b4b4: Te6edf3ListTff7b72<Te6edf3DeviceListEntryTff7b72>Tb4b4b4,
Te6edf3recentTcpDevicesTb4b4b4: Te6edf3ListTff7b72<Te6edf3DeviceListEntryTff7b72>Tb4b4b4,
Te6edf3usbDevicesTb4b4b4: Te6edf3ListTff7b72<Te6edf3DeviceListEntryTff7b72>Tb4b4b4,
Te6edf3connectionStatusTb4b4b4: Te6edf3ConnectionStatusTb4b4b4,
Te6edf3connectionProgressTb4b4b4: Tffa657String?Tb4b4b4,
Te6edf3onClickDisconnectTb4b4b4: Tb4b4b4(Tb4b4b4) Tff7b72-Tff7b72> Tffa657UnitTb4b4b4,
Tb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3selectedEntry Tff7b72=
Te6edf3bleDevicesTb4b4b4.Te6edf3find Tb4b4b4{ Tffa657itTb4b4b4.Te6edf3fullAddress Tff7b72=Tff7b72= Te6edf3selectedDevice Tb4b4b4}
Tff7b72?: Te6edf3discoveredTcpDevicesTb4b4b4.Te6edf3find Tb4b4b4{ Tffa657itTb4b4b4.Te6edf3fullAddress Tff7b72=Tff7b72= Te6edf3selectedDevice Tb4b4b4}
Tff7b72?: Te6edf3recentTcpDevicesTb4b4b4.Te6edf3find Tb4b4b4{ Tffa657itTb4b4b4.Te6edf3fullAddress Tff7b72=Tff7b72= Te6edf3selectedDevice Tb4b4b4}
Tff7b72?: Te6edf3usbDevicesTb4b4b4.Te6edf3find Tb4b4b4{ Tffa657itTb4b4b4.Te6edf3fullAddress Tff7b72=Tff7b72= Te6edf3selectedDevice Tb4b4b4}

T8b949e// Use the entry name if found in scan lists, otherwise fall back to the persisted name
T8b949e// from the last successful selection, and only show "Unknown Device" as a last resort.
Tff7b72val Te6edf3name Tff7b72= Te6edf3selectedEntryTff7b72?.Te6edf3name Tff7b72?: Te6edf3persistedDeviceName Tff7b72?: Te6edf3stringResourceTb4b4b4(Te6edf3ResTb4b4b4.Te6edf3stringTb4b4b4.Te6edf3unknown_deviceTb4b4b4)
Tff7b72val Te6edf3address Tff7b72= Te6edf3selectedEntryTff7b72?.Te6edf3address Tff7b72?: Te6edf3selectedDevice

Te6edf3ConnectingDeviceInfoTb4b4b4(
Te6edf3deviceName Tff7b72= Te6edf3nameTb4b4b4,
Te6edf3deviceAddress Tff7b72= Te6edf3addressTb4b4b4,
Te6edf3connectionStatus Tff7b72= Te6edf3connectionStatusTb4b4b4,
Te6edf3connectionProgress Tff7b72= Te6edf3connectionProgressTb4b4b4,
Te6edf3onClickDisconnect Tff7b72= Te6edf3onClickDisconnectTb4b4b4,
Tb4b4b4)
Tb4b4b4}

T8b949e/** Body for the NO_DEVICE state β€” sits inside the shared outer Card in [ConnectionsScreen]. */
Tf0883e@Composable
Tff7b72private Tff7b72fun Td2a8ffNoDeviceContentTb4b4b4(Tb4b4b4) Tb4b4b4{
Te6edf3ColumnTb4b4b4(
Te6edf3modifier Tff7b72= Te6edf3ModifierTb4b4b4.Te6edf3fillMaxWidthTb4b4b4(Tb4b4b4)Tb4b4b4.Te6edf3paddingTb4b4b4(T79c0ff1T79c0ff6.Te6edf3dpTb4b4b4)Tb4b4b4,
Te6edf3verticalArrangement Tff7b72= Te6edf3ArrangementTb4b4b4.Te6edf3CenterTb4b4b4,
Te6edf3horizontalAlignment Tff7b72= Te6edf3AlignmentTb4b4b4.Te6edf3CenterHorizontallyTb4b4b4,
Tb4b4b4) Tb4b4b4{
Te6edf3IconTb4b4b4(
Te6edf3imageVector Tff7b72= Te6edf3MeshtasticIconsTb4b4b4.Te6edf3NoDeviceTb4b4b4,
Te6edf3contentDescription Tff7b72= Tff7b72nullTb4b4b4,
Te6edf3modifier Tff7b72= Te6edf3ModifierTb4b4b4.Te6edf3sizeTb4b4b4(T79c0ff6T79c0ff4.Te6edf3dpTb4b4b4)Tb4b4b4,
Te6edf3tint Tff7b72= Te6edf3MaterialThemeTb4b4b4.Te6edf3colorSchemeTb4b4b4.Te6edf3outlineVariantTb4b4b4,
Tb4b4b4)
Te6edf3TextTb4b4b4(
Te6edf3text Tff7b72= Te6edf3stringResourceTb4b4b4(Te6edf3ResTb4b4b4.Te6edf3stringTb4b4b4.Te6edf3no_device_selectedTb4b4b4)Tb4b4b4,
Te6edf3modifier Tff7b72= Te6edf3ModifierTb4b4b4.Te6edf3paddingTb4b4b4(Te6edf3top Tff7b72= T79c0ff1T79c0ff6.Te6edf3dpTb4b4b4)Tb4b4b4,
Te6edf3style Tff7b72= Te6edf3MaterialThemeTb4b4b4.Te6edf3typographyTb4b4b4.Te6edf3bodyLargeTb4b4b4,
Te6edf3textAlign Tff7b72= Te6edf3TextAlignTb4b4b4.Te6edf3CenterTb4b4b4,
Te6edf3color Tff7b72= Te6edf3MaterialThemeTb4b4b4.Te6edf3colorSchemeTb4b4b4.Te6edf3outlineTb4b4b4,
Tb4b4b4)
Tb4b4b4}
Tb4b4b4}

T8b949e/** Visual state for the connection screen's [AnimatedContent] transition between the three card body variants. */
Tff7b72private Tff7b72enum Tff7b72class T56d364ConnectionUiState Tb4b4b4{
T8b949e/** No device is selected. */
Te6edf3NO_DEVICETb4b4b4,

T8b949e/** A device is selected or we are actively connecting. */
Te6edf3CONNECTINGTb4b4b4,

T8b949e/** Connected with node info available. */
Te6edf3CONNECTED_WITH_NODETb4b4b4,
Tb4b4b4}

Served by rngit 1.5.2 - Generated in 0.11s